//==========================================================================
// Copyright (c) 2000-2008,  Elastos, Inc.  All Rights Reserved.
//==========================================================================
#include "prxstub.h"
#include "rot.h"
#include <custmsh_server.h>
#include <aura.h>

EXTERN_C const InterfaceID EIID_IProxy;

#define STATUS_WIN32_ERROR(u) ((ECode)((u)|0x80070000))

#define E_ENTRY_NOT_FOUND         STATUS_WIN32_ERROR(1761)

#define ASSERT_PROXY(pProxy) \
    do {assert(((CObjectProxy*)pProxy)->m_cInterfaces > 0);} while (0);
#define GET_PROXY_CONTEXT(pProxy) \
    (((CObjectProxy*)pProxy)->m_pInterfaces[0].m_pContext)
#define KERNEL_IS_SERVER(pProxy) (GET_PROXY_CONTEXT(pProxy) == CTX_USER_2_KERN)

extern PVoid g_marshalLock;

static ECode StdUnmarshalCustomInterface(
    /* [in] */ ICustomMarshal *pCustomMarshal,
    /* [in] */ InterfacePack *pInterfacePack,
    /* [out] */ IInterface **ppObj)
{
    ECode ec;
    ICustomMarshal * pITempCustMsh;
    ClassID proxyClsid;
    wchar_t uunmbuf[80];
    proxyClsid.pUunm = uunmbuf;

    ec = pCustomMarshal->GetClsid(&proxyClsid);
    if (SUCCEEDED(ec)) {
        ec = _CObject_CreateInstance(proxyClsid, CTX_SAME_DOMAIN,
                EIID_ICustomMarshal, (IInterface **)&pITempCustMsh);
        if (SUCCEEDED(ec)) {
            ec = pITempCustMsh->CreateObject(pCustomMarshal, ppObj);
            pITempCustMsh->Release();
        }
    }

    return ec;
}

ECode StdMarshalInterface(
    /* [in] */ IInterface *pObj,
    /* [out] */ InterfacePack *pInterfacePack)
{
    ExportObject ex;
    ECode ec;
    IStub *pIStub;
    IProxy *pv;
    //ICustomMarshal *pCustomMarshal;

    AuraCriticalSection_Enter(g_marshalLock);
    ec = FindExportObject(pObj, &ex);
    if (SUCCEEDED(ec)) {
        ex.m_pIStub->GetOid(&pInterfacePack->m_oid);
        ec = CPseudoKernel::Process_GetCurrentId(&pInterfacePack->m_pid);
        if (FAILED(ec)) {
            AuraCriticalSection_Leave(g_marshalLock);
            return ec;
        }
        ex.m_pIStub->GetClsid(&pInterfacePack->m_clsid);
        ex.m_pIStub->GetClassEntry(&pInterfacePack->m_pClsEntry);
        ec = ex.m_pIStub->InterfaceIndex(pObj, &pInterfacePack->m_uIndex);
        AuraCriticalSection_Leave(g_marshalLock);
    }
    else {
        pv = (IProxy*)pObj->Probe(EIID_IProxy);
        if (pv) {
            AuraCriticalSection_Leave(g_marshalLock);
            pv->GetOid(&pInterfacePack->m_oid);
            ASSERT_PROXY(pv);
            pInterfacePack->m_pid = \
                KERNEL_IS_SERVER(pv)? KERNEL_PID : AURA_INVALID_PID;
            pv->GetClsid(&pInterfacePack->m_clsid);
            pv->GetClassEntry(&pInterfacePack->m_pClsEntry);
            ec = pv->QI_Index(&pObj, &(pInterfacePack->m_uIndex));

            CObjectProxy * pProxy = (CObjectProxy *)pv;
            ec = AttachServer(&pProxy->m_channelPool,
                              pInterfacePack->m_oid,
                              (pInterfacePack->m_pid == KERNEL_PID)
                                  ? CTX_USER_2_KERN :
                                    CTX_DIFF_PROCESS);

        }
        else {
            ec = CObjectStub::S_CreateObject(pObj, &pIStub);
            if (ec == E_ENTRY_NOT_FOUND) {
                ec = E_CONFLICT_WITH_LOCAL_KEYWORD;
            }
            if (SUCCEEDED(ec)) {
                pIStub->GetOid(&pInterfacePack->m_oid);
                CPseudoKernel::Process_GetCurrentId(&pInterfacePack->m_pid);
                pIStub->GetClsid(&pInterfacePack->m_clsid);
                pIStub->GetClassEntry(&pInterfacePack->m_pClsEntry);
                ec = pIStub->InterfaceIndex(pObj, &pInterfacePack->m_uIndex);
                if (ec == E_NO_INTERFACE) {
                    ec = E_CONFLICT_WITH_LOCAL_KEYWORD;
                }
            }
            AuraCriticalSection_Leave(g_marshalLock);
        }
    }

#if 0

    // Support extra runtime interface for custom marshaling
    pCustomMarshal = ICustomMarshal::Probe(pObj);
    if (FAILED(ec) && (pCustomMarshal)) {
        pInterfacePack->m_uIndex = 0xffffffff;
        ec = NOERROR;
    }
#endif

    return ec;
}

ECode StdUnmarshalInterface(
    /* [in] */ InterfacePack *pInterfacePack,
    /* [in] */ PContext pContext,
    /* [in] */ _ELASTOS Boolean bNameService,
    /* [out] */ IInterface **ppObj)
{
    ImportObject im;
    ExportObject ex;
    ECode ec;
    IProxy *pIProxy;
    ICustomMarshal * pCustomMarshal;

    AuraCriticalSection_Enter(g_marshalLock);
    ec = FindExportObject(pInterfacePack->m_oid, &ex);
    if (SUCCEEDED(ec)) {
        AuraCriticalSection_Leave(g_marshalLock);
        pCustomMarshal = ICustomMarshal::Probe(ex.m_pObject);
        if (!pCustomMarshal) {
            CObjectStub* pSt = (CObjectStub*)ex.m_pIStub;
            *ppObj = pSt->m_pInterfaces[pInterfacePack->m_uIndex].m_pObject;
            (*ppObj)->AddRef();
            ex.m_pIStub->Release();

            if (!bNameService) {
                ex.m_pIStub->Release();
            }

            return NOERROR;
        }
        else {
            ec = StdUnmarshalCustomInterface(\
                pCustomMarshal, pInterfacePack, ppObj);
            return ec;
        }
    }

    ec = FindImportObject(pInterfacePack->m_oid, &im);
    if (SUCCEEDED(ec)) {
        AuraCriticalSection_Leave(g_marshalLock);
        pCustomMarshal = ICustomMarshal::Probe(im.m_pIProxy);
        if (!pCustomMarshal) {
            ec = im.m_pIProxy->IndexQI(pInterfacePack->m_uIndex, ppObj);
        }
        else {
            ec = StdUnmarshalCustomInterface(\
                pCustomMarshal, pInterfacePack, ppObj);
        }
        im.m_pIProxy->Release();

        if (!bNameService) {
            CObjectProxy * pProxy = (CObjectProxy *)im.m_pIProxy;
            ec = DetachServer(&pProxy->m_channelPool,
                             pInterfacePack->m_oid,
                             (pInterfacePack->m_pid == KERNEL_PID)
                               ? CTX_USER_2_KERN : CTX_DIFF_PROCESS);

        }
    }
    else {

        ec = CObjectProxy::S_CreateObject(
                            pInterfacePack->m_oid,
                            pInterfacePack->m_clsid,
                            (UInt32 *)pInterfacePack->m_pClsEntry,
                            (pInterfacePack->m_pid == KERNEL_PID)? \
                                CTX_USER_2_KERN : CTX_DIFF_PROCESS,
                                &pIProxy);
        if (SUCCEEDED(ec)) {
            if (bNameService) {
                CObjectProxy * pProxy = (CObjectProxy *)pIProxy;
                ec = AttachServer(&pProxy->m_channelPool,
                                 pInterfacePack->m_oid,
                                 (pInterfacePack->m_pid == KERNEL_PID)
                                   ? CTX_USER_2_KERN : CTX_DIFF_PROCESS);
            }
            AuraCriticalSection_Leave(g_marshalLock);
            pCustomMarshal = ICustomMarshal::Probe(pIProxy);
            if (!pCustomMarshal) {
                ec = pIProxy->IndexQI(pInterfacePack->m_uIndex, ppObj);
            }
            else {
                ec = StdUnmarshalCustomInterface(\
                    pCustomMarshal, pInterfacePack, ppObj);
            }

            //BUGBUG:need release
            pIProxy->Release(); // Release IProxy's ref count
        }
        else {
            AuraCriticalSection_Leave(g_marshalLock);
        }
    }

    return ec;
}

#if defined(_DEBUG) || defined(_MARSHAL_DEBUG)
int _DumpGUID(REIID riid)
{
    return printf("%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X\n",
        riid.Data1, riid.Data2, riid.Data3,
        riid.Data4[0], riid.Data4[1], riid.Data4[2],
        riid.Data4[3], riid.Data4[4], riid.Data4[5],
        riid.Data4[6], riid.Data4[7]);
}

int _DumpCLSID(RClassID rclsid)
{
    _DumpGUID((REIID)rclsid);
    return printf("%S\n", rclsid.pUunm);
}

int _DumpBin(const void *pv, int size)
{
    int n, m;
    unsigned char *p = (unsigned char *)pv;

    printf("\n");
    for (n = 0; n < size; n++) {
        if (n != 0) {
            if ((n & 0xf) == 0) {
                printf("\t");
                for (m = 16; m > 0; m--) {
                    if (p[n - m] >= 0x20 && p[n - m] <= 0x7e) {
                        printf("%c", p[n-m]);
                    }
                    else {
                        printf(".");
                    }
                }
                printf("\n");
            }
            else if ((n & 0x07) == 0) {
                printf("- ");
            }
        }
        printf("%02X ", (UInt32)p[n]);
    }

    m = n & 0xf;
    if (!m) {
        m = 16;
    }
    else {
        for (int i = 0; i < 16 - m; i++) {
            printf("   ");
        }
        if (m <= 8) {
            printf("  ");
        }
    }
    printf("\t");

    while (m > 0) {
        if (p[n - m] >= 0x20 && p[n - m] <= 0x7e) {
            printf("%c", p[n-m]);
        }
        else {
            printf(".");
        }
        m--;
    }
    return printf("\n");
}
#endif // _DEBUG || _MARSHAL_DEBUG
